import utils.FileUtils;
import utils.SystemLookup;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.Random;

/**
 * java -Xss4m
 * */
public class QuickSort {
    private static long elapsedNanoTime = 0;
    private static BigInteger elapsedTime = BigInteger.ZERO;

    public static void main(String[] args) {

        if (args.length < 2 || args.length > 12) {
            System.out.println("Nieprawidlowa liczba parametrów!");
        }

        boolean magicImprovement = false;
        if (args[args.length-1].equals("r")) {
            magicImprovement = true;
        }

        boolean mixing = false;
        if (args[args.length-1].equals("m")) {
            mixing = true;
        }

        int numberOfExec = 0;
        try {
            numberOfExec = Integer.parseInt(args[0]);
            System.out.println("Liczba powtórzeń sortowania: " + numberOfExec );
        } catch (Exception ex) {
            System.out.println("Niepopawne parsowanie pierwszego parametru");
            System.exit(1);
        }

        String[] fileNames = Arrays.copyOfRange(args, 1, magicImprovement || mixing ? args.length -1 : args.length);
        System.out.println("Pliki: " + Arrays.toString(fileNames));

        for (String fileName : fileNames) {
            String[] out = FileUtils.readTextFileLine(SystemLookup.PATH_TO_LOAD + fileName + FileUtils.FILE_EXTENSION).split(" ");
            int[] arrayToSort = new int[out.length];
            System.out.println("Plik: " + fileName + "\tElementów: " + out.length);
            for (int i = 0; i < out.length; i++) {
                arrayToSort[i] = Integer.parseInt(out[i]);
            }

            for (int i = 0; i < numberOfExec; i++) {
                int[] intArray = Arrays.copyOf(arrayToSort, arrayToSort.length);
                long timeStart = System.nanoTime();
                if (magicImprovement) {
                    magicImprovement(intArray);
                    System.out.println("Magic attribute");
                }
                if (mixing) {
                    mixing(intArray);
                    System.out.println("Magic attribute");
                }
                quickSort(intArray, 0, out.length - 1);
                elapsedNanoTime = System.nanoTime() - timeStart;
                elapsedTime = elapsedTime.add(BigInteger.valueOf(elapsedNanoTime));
//                System.out.println(Arrays.toString(intArray));
            }
        }

        System.out.println("\nNano Time: " + elapsedTime.toString());
        System.out.println("Milliseconds Time: " + elapsedTime.divide(new BigInteger("1000000")));
        System.out.println("Seconds Time: " + elapsedTime.divide(new BigInteger("1000000000")));
        FileUtils.appendToFile("" + elapsedTime.divide(new BigInteger("1000000")));
    }

    public static void mixing(int[] a) {
        Random rand = new Random();
        for (int i = 1; i < a.length; i++) {
            int r = rand.nextInt(i);
            int ax = a[i];
            a[i] = a[r];
            a[r] = ax;
        }
    }

    public static void magicImprovement(int [] a) {
        Random rand = new Random();
        for (int j = 0; j < a.length; j++) {
            int r = rand.nextInt(a.length - 1);
            int a0 = a[0];
            a[0] = a[r];
            a[r] = a0;
        }
    }

    public static void quickSort(int[] a, int p, int r) {
        if (p < r) {
            int q = partition(a, p, r);
            quickSort(a, p, q);
            quickSort(a, q + 1, r);
        }
    }

    private static int partition(int[] a, int p, int r) {
        int x = a[p];
        int i = p - 1;
        int j = r + 1;

        while (true) {
            i++;
            while (i < r && a[i] < x)
                i++;
            j--;
            while (j > p && a[j] > x)
                j--;

            if (i < j) {
                int temp = a[i];
                a[i] = a[j];
                a[j] = temp;
            }
            else
                return j;
        }
    }

}
